
#ifndef LONGKEY_LONGKEY_PCAGENT_H_
#define LONGKEY_LONGKEY_PCAGENT_H_

#include <Windows.h>
#include <atlbase.h>
#include "../base/basictypes.h"
#include "../base/debug.h"
#include "pcagent_idl.h"

// TODO: Perhaps a better approach might be to use Adapter classes to
// modify behavior instead of the mode being explicitly specified here. Consider
// when making future changes.
// It might also be a good idea to rename this class and file since it is more
// a boilerplate for COM servers than it is related to "Longkey PCAgent" or the
// main PCAgent 3 COM server since it als handles various brokers.
class LongkeyPCAgent : public CAtlExeModuleT<LongkeyPCAgent>
{
public:
	enum ComServerMode
	{
		kPCAgent,
	};

	DECLARE_LIBID(LIBID_LongkeyPCAgentLib)

	explicit LongkeyPCAgent(bool is_machine, ComServerMode mode);
	~LongkeyPCAgent();

	HRESULT RegisterClassObjects(DWORD dwClsContext, DWORD dwFlags) throw();
	HRESULT RevokeClassObjects() throw();

	// The base ATL classes have an implicit dependency on the second parameter
	// to RegisterServer and UnregisterServer having a default value.
	HRESULT RegisterServer(BOOL rigister_tlb, const CLSID* id = NULL) throw();
	HRESULT UnregisterServer(BOOL unregister_tlb, const CLSID* id = NULL) throw();
	HRESULT PreMessageLoop(int show_cmd) throw();
	HRESULT PostMessageLoop() throw();
	HRESULT Main();

	// This is cloned from CAtlExeModuleT.Lock(). The one difference is the call
	// to ::CoAddRefServerProcess(). See the description for Unlock() below for
	// further information.
	virtual LONG Lock() throw()
	{
		::CoAddRefServerProcess();
		return CComGlobalsThreadModel::Increment(&m_nLockCnt);
	}

	// This is cloned from CAtlExeModuleT.Unlock(). The big difference is the call
	// to ::CoReleaseServerProcess(), to ensure that the class factories are
	// suspended once the lock count drops to zero. This fixes a race condition
	// where an activation request could come in the middle of shutting down.
	// This shutdown mechanism works with free threaded servers.
	//
	// There are race issues with the ATL delayed shutdown mechanism, hence the
	// associated code has been eliminated, and we have an assert to make sure
	// m_bDelayShutdown is not set.
	virtual LONG Unlock() throw()
	{
		ASSERT1(!m_bDelayShutdown);

		::CoReleaseServerProcess();
		LONG lRet = CComGlobalsThreadModel::Decrement(&m_nLockCnt);
		if (lRet == 0)
			::PostThreadMessage(m_dwMainThreadID, WM_QUIT, 0, 0);
		return lRet;
	}

private:
	_ATL_OBJMAP_ENTRY* GetObjectMap();
	HRESULT RegisterOrUnregisterExe(bool is_register);
	static HRESULT RegisterOrUnregisterExe(void* data, bool is_register);

	ComServerMode mode_;
	bool is_machine_;

	DISALLOW_EVIL_CONSTRUCTORS(LongkeyPCAgent);
};

#endif	// LONGKEY_LONGKEY_PCAGENT_H_